﻿// /***************************************************************************
//  * Buffer16BitStereo.cs
//  * Copyright (c) 2015 the authors.
//  * 
//  * All rights reserved. This program and the accompanying materials
//  * are made available under the terms of the GNU Lesser General Public License
//  * (LGPL) version 3 which accompanies this distribution, and is available at
//  * https://www.gnu.org/licenses/lgpl-3.0.en.html
//  *
//  * This library is distributed in the hope that it will be useful,
//  * but WITHOUT ANY WARRANTY; without even the implied warranty of
//  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//  * Lesser General Public License for more details.
//  *
//  ***************************************************************************/

using System;
using MP3Sharp.Decoding;

namespace MP3Sharp
{
    /// <summary>
    ///     Internal class used to queue samples that are being obtained from an Mp3 stream. 
    ///     This class handles stereo 16-bit data! Switch it out if you want mono or something.
    /// </summary>
    internal class BufferFloat : ABuffer
    {
        // This is stereo!
        public int CHANNELS = 2;
        // Write offset used in append_bytes
        private readonly float[] m_Buffer = new float[OBUFFERSIZE]; // all channels interleaved
        private readonly int[] m_Bufferp = new int[MAXCHANNELS]; // offset in each channel not same!
        // end marker, one past end of array. Same as bufferp[0], but
        // without the array bounds check.
        private int m_End;
        // Read offset used to read from the stream, in bytes.
        private int m_Offset;

        public BufferFloat(int channel = 2)
        {
            this.CHANNELS = channel;
            // Initialize the buffer pointers
            ClearBuffer();
        }

        /// <summary>
        ///     Gets the number of bytes remaining from the current position on the buffer.
        /// </summary>
        public int Left
        {
            get
            {
                return m_End - m_Offset;
            }
        }

        public int Read(float[] bufferOut, int offset, int count)
        {
            if (bufferOut == null)
            {
                throw new ArgumentNullException("bufferOut");
            }
            if ((count + offset) > bufferOut.Length)
            {
                throw new ArgumentException("The sum of offset and count is larger than the buffer length");
            }
            int remaining = Left;
            int copySize;
            if (count > remaining)
            {
                copySize = remaining;
            }
            else
            {
                // Copy an even number of sample frames
                //int remainder = count % ( CHANNELS);
                copySize = count;
            }

            Array.Copy(m_Buffer, m_Offset, bufferOut, offset, copySize);

            m_Offset += copySize;
            return copySize;
        }

        public override void AppendSamples(int channel, float[] f)
        {
            for (var i = 0; i < f.Length; i++)
            {
                m_Buffer[m_Bufferp[channel] + i * CHANNELS] = f[i];
            }
            m_Bufferp[channel] += CHANNELS * f.Length;

        }
        /// <summary>
        ///     This implementation does not clear the buffer.
        /// </summary>
        public override sealed void ClearBuffer()
        {
            m_Offset = 0;
            m_End = 0;

            for (int i = 0; i < CHANNELS; i++)
                m_Bufferp[i] = i * 2; // two bytes per channel
        }

        public override void SetStopFlag()
        {
        }

        public override void WriteBuffer(int val)
        {
            m_Offset = 0;

            // speed optimization - save end marker, and avoid
            // array access at read time. Can you believe this saves
            // like 1-2% of the cpu on a PIII? I guess allocating
            // that temporary "new int(0)" is expensive, too.
            m_End = m_Bufferp[0];
        }

        public override void Close()
        {
        }
    }
}
